package com.liang.recommend.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.liang.recommend.algorithm.RandomRecommend;
import com.liang.recommend.algorithm.Vote;
import com.liang.recommend.mapper.*;
import com.liang.recommend.pojo.*;
import com.liang.recommend.recommend.BashRecommend;
import com.liang.recommend.service.MovieService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.liang.recommend.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 小梁
 * @since 2020-12-03
 */
@Service
public class MovieServiceImpl extends ServiceImpl<MovieMapper, Movie> implements MovieService {
    @Autowired
    UserActionMapper userActionMapper;
    @Autowired
    UserCoordinationMapper userCoordinationMapper;
    @Autowired
    UserCoordination2Mapper userCoordination2Mapper;
    @Autowired
    MovieMapper movieMapper;
    @Autowired
    BashRecommend bashRecommend;
    @Autowired
    LatentFactorMapper latentFactorMapper;
    @Autowired
    MovieCoordinationMapper movieCoordinationMapper;
    @Autowired
    Vote vote;
    @Autowired
    RedisUtil redisUtil;

    private List<Movie> temp; //提高作用域

    /**
     * 根据权重计算得到完整的推荐电影列表（放redis也行，数据不多我就算了）
     * @param userId
     * @return
     */
    @Override
    public List<Movie> getRecommend(Long userId,int index) {
        int pages=1;//对应向下取邻居
        List<Long> result;
        List<Long> get;
        List<Movie> movies=new ArrayList<Movie>();
        List<Long> A = getMovieIdList1(userId, pages);
        List<Long> B = getMovieList3(userId);
        List<Long> C= getMovieIdList2(userId, pages);
        result = vote.voteResult(A, B, C);
        get=result.subList((index-1)*5,(index-1)*5+5);
        QueryWrapper<Movie> wrapper = new QueryWrapper<>();
        wrapper.in("m_id",get);
        temp = movieMapper.selectList(wrapper);
        for (Movie movie : temp) {
            movies.add(movie);
        }
        if (get.size()<5){
            index=1;
            pages+=1; //使邻居向下
            movies.add(temp.get(0));
            A = getMovieIdList1(userId, pages);
            C = getMovieIdList2(userId, pages);
            result = vote.voteResult(A, B, C);
            get=result.subList((index-1)*5,(index-1)*5+5);
            QueryWrapper<Movie> wrapper2 = new QueryWrapper<>();
            wrapper2.in("m_id",get);
            this.temp = movieMapper.selectList(wrapper2);//更新temp
            if (temp.size()<5){ //向下邻居都不够时
                RandomRecommend randomRecommend = new RandomRecommend();
                List<Integer> random = randomRecommend.getRandom();
                QueryWrapper<Movie> wrapper1 = new QueryWrapper<>();
                wrapper1.in("m_id",random);
                List<Movie> list = movieMapper.selectList(wrapper1);
                temp.addAll(list);
                temp.subList(0,5);
            }
        }


        return movies;
    }

    /**
     * 根据Euclidean_metric得到推荐电影列表
     * @param userId
     * @param pages
     * @return List<Movie>
     */
    @Override
    public List<Movie> getMovieList1(Long userId, int pages) {
        List<Long> list = new ArrayList<>(); //相邻用户id列表
        List<Long> listMovies = new ArrayList<>(); //相邻用户的高分电影id列表
        Page<UserCoordination> page = new Page<>(pages, 3);
        QueryWrapper<UserCoordination> wrapper = new QueryWrapper<UserCoordination>()
                .eq("user_id1", userId)
                .orderByAsc ("distance");//小--->大
        List<UserCoordination> records = userCoordinationMapper.selectPage(page,wrapper).getRecords();
        for (UserCoordination record : records) {
            list.add(record.getUserId2());
        }

        QueryWrapper<UserAction> wrapper2 = new QueryWrapper<UserAction>();
        wrapper2.select("DISTINCT movie_id")
                .in("user_id", list)
                .gt("score", 2.6);

        List<UserAction> userActions = userActionMapper.selectList(wrapper2);

        for (UserAction userAction : userActions) {
            if (userAction.getMovieId()!=null){
                listMovies.add(userAction.getMovieId());
            }
        }

        QueryWrapper<Movie> wrapper3 = new QueryWrapper<Movie>();
        wrapper3.in("m_id", listMovies);
        List<Movie> movies = movieMapper.selectList(wrapper3);
        return bashRecommend.getRecommendMovieList(userId, movies);
    }


    /**
     * 根据Euclidean_metric得到推荐电影id列表
     * @param userId
     * @param pages
     * @return List<Movie>
     */
    @Override
    public List<Long> getMovieIdList1(Long userId, int pages) {
        List<Long> list = new ArrayList<>(); //相邻用户id列表
        List<Long> listMovies = new ArrayList<>(); //相邻用户的高分电影id列表
        Page<UserCoordination> page = new Page<>(pages, 3);
        QueryWrapper<UserCoordination> wrapper = new QueryWrapper<UserCoordination>()
                .eq("user_id1", userId)
                .orderByAsc ("distance");//小--->大
        List<UserCoordination> records = userCoordinationMapper.selectPage(page,wrapper).getRecords();
        for (UserCoordination record : records) {
            list.add(record.getUserId2());
        }

        QueryWrapper<UserAction> wrapper2 = new QueryWrapper<UserAction>();
        wrapper2.select("distinct movie_id")
                .in("user_id", list)
                .gt("score", 2.6); //即为评分达到4.0

        List<UserAction> userActions = userActionMapper.selectList(wrapper2);
        for (UserAction userAction : userActions) {
                listMovies.add(userAction.getMovieId());
        }
        return bashRecommend.getRecommendMovieIdList(userId,listMovies);
    }


    /**
     * 根据Pearson得到推荐电影id列表
     * @param userId
     * @param pages
     * @return List<Long>
     */
    @Override
    public List<Long> getMovieIdList2(Long userId, int pages) {
        List<Long> listMovies = new ArrayList<>();//总推荐
        if (userId != 0) {
            List<Long> listP = new ArrayList<>(); //正相关用户id列表
            List<Long> listN = new ArrayList<>(); //负相关用户id列表
            Set<Long> setP = new HashSet<>();
            Set<Long> setN = new HashSet<>();

            Page<UserCoordination2> page = new Page<>(pages, 3);
            QueryWrapper<UserCoordination2> wrapper = new QueryWrapper<UserCoordination2>();

            wrapper.eq("user_id1", userId)
                   .orderByDesc("distance"); //高--->低

            List<UserCoordination2> records = userCoordination2Mapper.selectPage(page, wrapper).getRecords();

            for (UserCoordination2 record : records) {
                if (record.getType() == 0) {  //0是正相关相似
                    listP.add(record.getUserId2());
                } else { //1是负相关相似
                    listN.add(record.getUserId2());
                }
            }
            /*
            正相关
             */
            List<UserAction> userActions = new ArrayList<>(); //正相关相似用户的高分电影列表
            if (listP.size() != 0) {
                QueryWrapper<UserAction> wrapperP = new QueryWrapper<UserAction>(); //正相关条件构造器
                wrapperP.in("user_id", listP)
                        .gt("score", 2.6); //看过电影且评了4.0以上
                userActions = userActionMapper.selectList(wrapperP);
                setP = new HashSet<>();
                for (UserAction action : userActions) {
                    setP.add(action.getMovieId());
                }
            }
            /*
            负相关
             */
            List<UserAction> userActions1 = new ArrayList<>(); //负相关相似用户的高分电影列表
            if (listN.size() != 0) {
                QueryWrapper<UserAction> wrapperN = new QueryWrapper<UserAction>(); //负相关条件构造器
                wrapperN.in("user_id", listN)
                        .lt("score", 2.2); //看过电影评分低于3分或者没评分
                userActions1 = userActionMapper.selectList(wrapperN);
                setN = new HashSet<>();
                for (UserAction action : userActions1) {
                    setN.add(action.getMovieId());
                }
            }

            if (setP.size() != 0 || setN.size() != 0) { //任意一个电影列表不为空
                setP.addAll(setN);
                for (Long movieId : setP) {
                        listMovies.add(movieId);
                }

            }
        }
        return bashRecommend.getRecommendMovieIdList(userId,listMovies);
    }


    /**
     * 根据Latent_Factor得到推荐电影id列表
     * @return List<Movie>
     */
    @Override
    public List<Long> getMovieList3(Long userId) {
        List<Long> listMovies = new ArrayList<Long>();
        QueryWrapper<LatentFactor> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",userId).orderByDesc("value");//高得分
        List<LatentFactor> latentFactors = latentFactorMapper.selectList(wrapper);
        for (LatentFactor latentFactor : latentFactors) {
            listMovies.add(latentFactor.getMovieId());
        }
        return listMovies; //不需要bashRecommend因为它本来就已经排除已经看过的电影
    }


    /**
     * 得到随机的电影推荐列表
     * @return List<Movie>
     */
    @Override
    public List<Integer> getMovieList4() {
        RandomRecommend randomRecommend = new RandomRecommend();
        List<Integer> random = randomRecommend.getRandom();
        return random;
    }

    @Override
    public List<Long> getMovieList5(Long userId) {
        String key = Long.toString(userId);
        List<Object> objects = redisUtil.lGet(key, 0, -1);
        List<Long> list = new ArrayList<>();
        for (Object object : objects) {
            list.add(Long.valueOf(String.valueOf(object)));
        }
        QueryWrapper<MovieCoordination> wrapper = new QueryWrapper<>();
        wrapper
                .select("distinct movie_id2")
                .in("movie_id1",list).orderByDesc("distance");
        List<MovieCoordination> moviesList = movieCoordinationMapper.selectList(wrapper);
        List<Long> movies = new ArrayList<>();
        for (MovieCoordination coordination : moviesList) {
            movies.add(coordination.getMovieId2());
        }
        return bashRecommend.getRecommendMovieIdList(userId,movies); //去重
    }

}
